package services

import (
	"bankend/config"
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/silenceper/wechat/v2"
	cache "github.com/silenceper/wechat/v2/cache"
	"github.com/silenceper/wechat/v2/officialaccount"
	oaConfig "github.com/silenceper/wechat/v2/officialaccount/config"
	oamessage "github.com/silenceper/wechat/v2/officialaccount/message"
)

// —— access_token 管理部分 ——

type tokenResponse struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int    `json:"expires_in"`
	ErrCode     int    `json:"errcode,omitempty"`
	ErrMsg      string `json:"errmsg,omitempty"`
}

var (
	tokenMu     sync.RWMutex
	cachedToken string
	expiresAt   time.Time
)

// GetAccessToken 返回一个有效的 access_token：
//   - 如果本地缓存还没过期，直接返回缓存。
//   - 否则重新请求微信接口并更新缓存。
func GetAccessToken() (string, error) {
	tokenMu.RLock()
	if cachedToken != "" && time.Now().Before(expiresAt) {
		t := cachedToken
		tokenMu.RUnlock()
		return t, nil
	}
	tokenMu.RUnlock()

	// 缓存失效，去微信服务器拿新的
	url := fmt.Sprintf(
		"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
		config.AppConfig.WxAppID,
		config.AppConfig.WxSecret,
	)

	resp, err := http.Get(url)
	if err != nil {
		return "", fmt.Errorf("请求 access_token 失败: %w", err)
	}
	defer resp.Body.Close()

	var tr tokenResponse
	if err := json.NewDecoder(resp.Body).Decode(&tr); err != nil {
		return "", fmt.Errorf("解析 access_token 响应失败: %w", err)
	}
	if tr.ErrCode != 0 {
		return "", fmt.Errorf("微信返回错误: %d %s", tr.ErrCode, tr.ErrMsg)
	}

	// 更新缓存：提前 5 分钟过期以防止刚好临界
	tokenMu.Lock()
	cachedToken = tr.AccessToken
	expiresAt = time.Now().Add(time.Duration(tr.ExpiresIn-300) * time.Second)
	tokenMu.Unlock()

	return cachedToken, nil
}

// ReadAccessToken 只读本地缓存的 access_token，未就绪或过期则报错
func ReadAccessToken() (string, error) {
	tokenMu.RLock()
	defer tokenMu.RUnlock()
	if cachedToken == "" || time.Now().After(expiresAt) {
		return "", fmt.Errorf("access_token 未就绪或已过期，请先预热缓存")
	}
	return cachedToken, nil
}

// —— 模板消息推送部分 ——

type TemplateMessageData struct {
	OpenID      string            // 接收用户的 OpenID
	TemplateID  string            // 微信后台模板 ID
	URL         string            // 点击后跳转链接，可空
	MiniProgram *MiniProgramData  // 小程序跳转配置，可 nil
	ClientMsgID string            // client_msg_id，可空
	Data        map[string]string // 模板数据：first/keyword*/remark
}

type MiniProgramData struct {
	AppID    string
	PagePath string
}

type sendResp struct {
	ErrCode int    `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
	MsgID   int64  `json:"msgid"`
}

// SendTemplateMessage 下发模板消息给单个用户（仅使用本地缓存的 token）
func SendTemplateMessage(params TemplateMessageData) error {
	// 1. 读取缓存 access_token
	token, err := ReadAccessToken()
	if err != nil {
		return fmt.Errorf("读取 access_token 失败: %w", err)
	}

	// 2. 构造请求 URL
	apiURL := fmt.Sprintf(
		"https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s",
		token,
	)

	// 3. 构造请求体
	payload := map[string]interface{}{
		"touser":      params.OpenID,
		"template_id": params.TemplateID,
	}
	if params.URL != "" {
		payload["url"] = params.URL
	}
	if params.ClientMsgID != "" {
		payload["client_msg_id"] = params.ClientMsgID
	}
	if params.MiniProgram != nil {
		payload["miniprogram"] = map[string]string{
			"appid":    params.MiniProgram.AppID,
			"pagepath": params.MiniProgram.PagePath,
		}
	}
	dataMap := make(map[string]map[string]string, len(params.Data))
	for k, v := range params.Data {
		dataMap[k] = map[string]string{"value": v}
	}
	payload["data"] = dataMap

	// 4. 序列化 & 日志打印
	bodyBytes, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("序列化 payload 失败: %w", err)
	}
	log.Printf("[SendTemplateMessage] URL: %s", apiURL)
	log.Printf("[SendTemplateMessage] Body: %s", string(bodyBytes))

	// 5. 发起请求
	resp, err := http.Post(apiURL, "application/json", bytes.NewBuffer(bodyBytes))
	if err != nil {
		return fmt.Errorf("微信推送请求失败: %w", err)
	}
	defer resp.Body.Close()

	// 6. 读取并打印响应
	respBody, _ := ioutil.ReadAll(resp.Body)
	log.Printf("[SendTemplateMessage] Status: %s, Response: %s", resp.Status, string(respBody))

	// 7. 解析并返回错误
	var sr sendResp
	if err := json.Unmarshal(respBody, &sr); err != nil {
		return fmt.Errorf("解析微信推送响应失败: %w", err)
	}
	if sr.ErrCode != 0 {
		return fmt.Errorf("微信返回错误 %d: %s", sr.ErrCode, sr.ErrMsg)
	}
	return nil
}

// —— SDK 封装部分 ——

// getOA 初始化并返回 OfficialAccount 实例，使用内存缓存
func getOA() *officialaccount.OfficialAccount {
	wc := wechat.NewWechat()
	mpCfg := &oaConfig.Config{
		AppID:     config.AppConfig.WxAppID,
		AppSecret: config.AppConfig.WxSecret,
		Cache:     cache.NewMemory(), // 使用内存缓存，避免 panic("cache is ineed")
	}
	return wc.GetOfficialAccount(mpCfg)
}

// WxService 保存模板消息客户端
// 它封装了发送模板消息和管理模板的方法
type WxService struct {
	tpl *oamessage.Template
}

// NewWxService 初始化模板消息客户端
func NewWxService() *WxService {
	oa := getOA()
	return &WxService{tpl: oa.GetTemplate()}
}

// SendOrderNotify 发送"下单通知"模板消息
func (s *WxService) SendOrderNotify(openid, orderNo, serviceName string) error {
	now := time.Now().Format("2006-01-02 15:04")
	msg := oamessage.TemplateMessage{
		ToUser:     openid,
		TemplateID: "Ze65oKh9WP9axtXRRRoZui6IdzmjkU3dpq6ogJh8Bew",
		Data: map[string]*oamessage.TemplateDataItem{
			"first":    {Value: "您有一个新的订货单", Color: "#000000"},
			"keyword1": {Value: orderNo},
			"keyword2": {Value: serviceName},
			"keyword3": {Value: now},
			"remark":   {Value: "请登录系统查看订单详情并及时配货"},
		},
	}
	_, err := s.tpl.Send(&msg)
	return err
}

// GetTokenExpireTime 返回当前缓存的token过期时间
func GetTokenExpireTime() time.Time {
	tokenMu.RLock()
	defer tokenMu.RUnlock()
	return expiresAt
}
